package org.dicr.util.jds.module.gui;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.List;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.tree.*;

import org.dicr.event.*;
import org.dicr.gui.*;
import org.dicr.gui.layout.*;
import org.dicr.util.jds.conn.*;
import org.dicr.util.jds.conn.gui.*;
import org.dicr.util.jds.module.*;

import com.borland.dx.dataset.*;
import com.borland.dx.sql.dataset.*;

/***********************************************************************************************************************
 * @author Igor A Tarasov me@dicr.dn.ua
 * @version 030120
 **********************************************************************************************************************/
public class DicrDataModulePanel extends JPanel implements DicrDialogContent {
	private static final long serialVersionUID = 1L;

	DicrDataModule datamodule = null;

	private DataModuleListener damolis = new DataModuleListener();

	private JScrollPane treeScroll = new JScrollPane();

	private JTree tree = new JTree();

	private JScrollPane logScroll = new JScrollPane();

	MessagesPane log = new MessagesPane();

	private JPanel controlPanel = new JPanel();

	boolean ignoreControls = false;

	private JPanel connPanel = new JPanel();

	JButton connCreateButton = new JButton("Создать базу данных...");

	private JLabel connCurrLabel = new JLabel("Текущее:");

	private JTextField connCurrText = new JTextField();

	JButton connSelectButton = new JButton("...");

	JButton connConnectButton = new JButton("Подключить");

	JButton connDisconnectButton = new JButton("Отключить");

	private JPanel cachPanel = new JPanel();

	private JLabel cachFileLabel = new JLabel("Файл кэша:");

	JTextField cachNameText = new JTextField();

	JButton cachChangeButton = new JButton("...");

	JButton cachEnableButton = new JButton("Включить");

	JButton cachDisableButton = new JButton("Отключить");

	private JPanel tabPanel = new JPanel();

	JButton tabCreateButton = new JButton("Создать таблицы");

	JButton tabOpenButton = new JButton("Открыть таблицы");

	JButton tabCloseButton = new JButton("Закрыть таблицы");

	JButton tabSaveButton = new JButton("Сохранить изменения");

	JButton tabRefreshButton = new JButton("Обновить изменения");

	/*******************************************************************************************************************
	 * Конструктор
	 * 
	 * @param theDatamodule data module
	 ******************************************************************************************************************/
	public DicrDataModulePanel(DicrDataModule theDatamodule) {
		try {
			jbInit(theDatamodule);
		} catch (Exception ex) {
			DicrExceptionHandler.handleException(ex, this);
		}
	}

	/*******************************************************************************************************************
	 * Конструктор
	 ******************************************************************************************************************/
	public DicrDataModulePanel() {
		this(null);
	}

	/*******************************************************************************************************************
	 * Инициализация
	 ******************************************************************************************************************/
	private void jbInit(DicrDataModule theDatamodule) {
		this.setLayout(new PnutsLayout(2, 2, 2));
		this.add(this.treeScroll, "halign=fill, valign=fill");
		this.add(this.controlPanel, "halign=fill, valign=fill, expand=x");
		this.add(this.logScroll, "halign=fill, valign=fill, expand=y, colspan=2");

		this.treeScroll.setViewportView(this.tree);
		this.tree.setToolTipText("Таблицы модуля данных.");
		this.tree.setVisibleRowCount(5);

		this.logScroll.setViewportView(this.log);

		this.controlPanel.setLayout(new PnutsLayout("cols=2, padx=4, pady=4, halign=fill, valign=fill"));
		this.controlPanel.add(this.connPanel, "expand=x");
		this.controlPanel.add(this.tabPanel, "rowspan=2");
		this.controlPanel.add(this.cachPanel, "expand=x");

		JPanel tmp = null;
		ControlsListener conlis = new ControlsListener();

		this.connPanel.setBorder(new CompoundBorder(new TitledBorder("Соединение"), new EmptyBorder(5, 5, 5, 5)));
		this.connPanel.setLayout(new PnutsLayout(3, 2, 2));
		this.connPanel.add(this.connCurrLabel, "halign=left");
		tmp = new JPanel(new BorderLayout(2, 0));
		tmp.add(this.connCurrText, BorderLayout.CENTER);
		tmp.add(this.connSelectButton, BorderLayout.EAST);
		this.connPanel.add(tmp, "halign=fill, expand=x, colspan=2");
		this.connPanel.add(Box.createHorizontalStrut(1));
		this.connPanel.add(this.connCreateButton, "halign=fill, expand=x, colspan=2");
		this.connPanel.add(Box.createHorizontalStrut(1));
		this.connPanel.add(this.connConnectButton, "halign=fill, expand=x");
		this.connPanel.add(this.connDisconnectButton, "halign=fill, expand=x");
		this.connCurrText.setEditable(false);
		this.connCurrText.setColumns(1);
		this.connCurrText.setToolTipText("<HTML>Текущие параметры соединения модуля данных.</HTML>");
		this.connSelectButton.setToolTipText("<HTML>Выбрать соединение.<BR>При помощи менеджера соединений выберите существующее или создайте новое соединение.</HTML>");
		this.connSelectButton.setMargin(new Insets(0, 0, 0, 0));
		this.connSelectButton.addActionListener(conlis);
		this.connCreateButton.setToolTipText("<HTML>Создать файлы базы данных на локальном диске.<BR>Если файлы уже существуют, то они не пересоздаются.<BR>Для удаления базы данных удалите все файлы (базу данных, логи, временные файлы) вручную.</HTML>");
		this.connCreateButton.addActionListener(conlis);
		this.connConnectButton.setToolTipText("<HTML>Установить соединение с базой данных.<BR>После соединения доступны функции обмена данными с базой.</HTML>");
		this.connConnectButton.addActionListener(conlis);
		this.connDisconnectButton.setToolTipText("<HTML>Разорвать соединение с базой данных.<BR>Для работы без подключения к базе данных необходимо включить локальный кэш.</HTML>");
		this.connDisconnectButton.addActionListener(conlis);

		this.cachPanel.setBorder(new CompoundBorder(new TitledBorder("Кэш"), new EmptyBorder(5, 5, 5, 5)));
		this.cachPanel.setLayout(new PnutsLayout(3, 2, 2));
		this.cachPanel.add(this.cachFileLabel, "halign=left");
		tmp = new JPanel(new BorderLayout(2, 0));
		tmp.add(this.cachNameText, BorderLayout.CENTER);
		tmp.add(this.cachChangeButton, BorderLayout.EAST);
		this.cachPanel.add(tmp, "halign=fill, expand=x, colspan=2");
		this.cachPanel.add(Box.createHorizontalStrut(1));
		this.cachPanel.add(this.cachEnableButton, "halign=fill, expand=x");
		this.cachPanel.add(this.cachDisableButton, "halign=fill, expand=x");

		this.cachNameText.setColumns(1);
		this.cachNameText.setToolTipText("<HTML>Полный путь-имя файла текущего локального кэша.<BR>Кэш создается на локальном диске и позволяет ускорить работу с базой данных и работать без подключения.</HTML>");
		this.cachChangeButton.setToolTipText("<HTML>Выбрать новое имя файла кэша.<BR>Если указанный файл не существует, то создается новый.<BR>Если файл уже существует, то он будет использован для кэша.</HTML>");
		this.cachChangeButton.setMargin(new Insets(0, 0, 0, 0));
		this.cachChangeButton.addActionListener(conlis);
		this.cachEnableButton.setToolTipText("<HTML>Включить использование локального кэша.<BR>Позволяет работать с данными таблиц без соединения с базой,<BR>сохранять данные на диске при завершении программы,<BR>ускоряет работу с удаленной базой данных.</HTML>");
		this.cachEnableButton.addActionListener(conlis);
		this.cachDisableButton.setToolTipText("<HTML>Отключить использование локального кэша.<BR>При отключенном кэше работать можно только при установленном соединении с базой данных.<BR>Все несохраненные данные при выходе программы теряются.</HTML>");
		this.cachDisableButton.addActionListener(conlis);

		this.tabPanel.setBorder(new CompoundBorder(new TitledBorder("Таблицы"), new EmptyBorder(5, 5, 5, 5)));
		this.tabPanel.setLayout(new GridLayout(5, 1, 2, 2));
		this.tabPanel.add(this.tabCreateButton);
		this.tabPanel.add(this.tabOpenButton);
		this.tabPanel.add(this.tabCloseButton);
		this.tabPanel.add(this.tabSaveButton);
		this.tabPanel.add(this.tabRefreshButton);
		this.tabCreateButton.setToolTipText("<HTML>Создать таблицы в базе данных.<BR>Требуется подключение к базе данных.</HTML>");
		this.tabCreateButton.addActionListener(conlis);
		this.tabOpenButton.setToolTipText("<HTML>Открыть таблицы.<BR>Операции с данными возможны только при открытых таблицах.<BR>Закрывайте таблицы только в особых случаях.<HTML>");
		this.tabOpenButton.addActionListener(conlis);
		this.tabCloseButton.setToolTipText("<HTML>Закрыть таблицы.<BR>Требуется только при структурных операциях (например переключение кэша).</HTML>");
		this.tabCloseButton.addActionListener(conlis);
		this.tabSaveButton.setToolTipText("<HTML>Сохранить изменения.<BR>Все изменения данных передаются в базу данных.<BR>При отключенном кэше изменения не сохраняются при выходе из программы.<BR>Требуется соединение с базой данных.</HTML>");
		this.tabSaveButton.addActionListener(conlis);
		this.tabRefreshButton.setToolTipText("<HTML>Обновить изменения.<BR>Принимаются все изменения данных из базы данных.<BR>Все несохраненные изменения будут утеряны.<BR>Рекомендуется сначала сохранить текущие изменения.<BR>Требуется соединение с базой данных.</HTML>");
		this.tabRefreshButton.addActionListener(conlis);

		setDataModule(theDatamodule);

		updateControls();
	}

	/*******************************************************************************************************************
	 * Обновление состояния элементов управлегния
	 ******************************************************************************************************************/
	protected void updateControls() {
		try {
			this.ignoreControls = true;
			if (this.datamodule == null) {
				this.connCreateButton.setEnabled(false);
				this.tabCreateButton.setEnabled(false);
				this.tabOpenButton.setEnabled(false);
				this.tabCloseButton.setEnabled(false);
				this.tabSaveButton.setEnabled(false);
				this.tabRefreshButton.setEnabled(false);
				this.cachNameText.setEnabled(false);
				this.cachChangeButton.setEnabled(false);
				this.cachEnableButton.setEnabled(false);
				this.cachDisableButton.setEnabled(false);
				this.connCurrText.setEnabled(false);
				this.connSelectButton.setEnabled(false);
				this.connConnectButton.setEnabled(false);
				this.connDisconnectButton.setEnabled(false);
			} else {
				boolean connected = this.datamodule.isConnected();
				ConnectionElement connElement = this.datamodule.getConnectionElement();
				boolean cachEnabled = this.datamodule.isCachEnabled();
				String cachName = this.datamodule.getCachFileName();

				this.connCreateButton.setEnabled(!connected);
				this.tabCreateButton.setEnabled(connected);

				this.tabOpenButton.setEnabled(connected || cachEnabled);
				this.tabCloseButton.setEnabled(connected || cachEnabled);
				this.tabSaveButton.setEnabled(connected);
				this.tabRefreshButton.setEnabled(connected);

				this.cachNameText.setEnabled(true);
				this.cachNameText.setEditable(!cachEnabled);
				this.cachNameText.setText(cachName != null ? cachName : "");
				this.cachChangeButton.setEnabled(!cachEnabled);
				this.cachEnableButton.setEnabled(!cachEnabled);
				this.cachDisableButton.setEnabled(cachEnabled);

				this.connCurrText.setEnabled(true);
				this.connCurrText.setText(connElement != null ? connElement.getDescriptor().getConnectionURL() : "");
				this.connSelectButton.setEnabled(!connected);
				this.connConnectButton.setEnabled(!connected);
				this.connDisconnectButton.setEnabled(connected);
			}
		} finally {
			this.ignoreControls = false;
		}
	}

	/*******************************************************************************************************************
	 * Установка модуля данных
	 * 
	 * @param theDatamodule data module
	 ******************************************************************************************************************/
	public void setDataModule(DicrDataModule theDatamodule) {
		if (this.datamodule != theDatamodule) {
			if (this.datamodule != null) this.datamodule.removeProgramListener(this.damolis);
			this.datamodule = theDatamodule;
			DefaultMutableTreeNode root = new DefaultMutableTreeNode("Модуль данных");
			if (theDatamodule != null) {
				theDatamodule.addProgramListener(this.damolis);
				StorageDataSet[] datasets = theDatamodule.getStorageDataSets();
				if (datasets != null) {
					for (int i = 0; i < datasets.length; i++) {
						root.add(new DefaultMutableTreeNode(datasets[i].getTableName()));
					}
				}
			}
			this.tree.setModel(new DefaultTreeModel(root));
			updateControls();
		}
	}

	/*******************************************************************************************************************
	 * Возвращает модуль данных
	 * 
	 * @return data module
	 ******************************************************************************************************************/
	public DicrDataModule getDataModule() {
		return this.datamodule;
	}

	/*******************************************************************************************************************
	 * ######################################################################### Слушатель элементов управления
	 * #########################################################################
	 ******************************************************************************************************************/
	class ControlsListener implements ActionListener {
		/**
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		@Override
        public void actionPerformed(ActionEvent e) {
			if (DicrDataModulePanel.this.ignoreControls || DicrDataModulePanel.this.datamodule == null) { return; }
			try {
				setCursor(GuiMisc.CURSOR_WAIT);
				Object src = e.getSource();
				if (src == DicrDataModulePanel.this.connCreateButton) {
					CustomFileFilter filter = new CustomFileFilter();
					filter.addExt("jds");
					filter.setDescription(".jds - файлы базы данных DataStore");
					JFileChooser chooser = new JFileChooser();
					ConnectionElement conn = DicrDataModulePanel.this.datamodule.getConnectionElement();
					if (conn != null) {
						ConnectionDescriptor desc = conn.getDescriptor();
						if (desc != null) {
							String url = desc.getConnectionURL();
							if (url != null) {
								chooser.setSelectedFile(new File(new DataStoreURL(url).getFile()));
							}
						}
					}
					chooser.setAcceptAllFileFilterUsed(true);
					chooser.setFileFilter(filter);
					chooser.setDialogTitle("Создание базы данных. Выберите имя нового файла.");
					chooser.setDialogType(JFileChooser.SAVE_DIALOG);
					chooser.setApproveButtonText("Создать");
					while (true) {
						int code = chooser.showSaveDialog(DicrDataModulePanel.this);
						if (code != JFileChooser.APPROVE_OPTION) break;
						File file = chooser.getSelectedFile();
						if (file == null) break;
						if (file.exists()) {
							JOptionPane.showMessageDialog(DicrDataModulePanel.this, new Object[] {
							        "Файл уже существует. Сначала удалите файлы базы данных", file.getPath() },
							        "Ошибка создания базы данных", JOptionPane.ERROR_MESSAGE);
							continue;
						}
						try {
							DicrDataModulePanel.this.log.addMessage("\nСоздание базы данных: " + file.getPath());
							DicrDataModulePanel.this.datamodule.createDataBase(file.getPath());
						} catch (Exception ex) {
							DicrDataModulePanel.this.log.addErrorMessage(ex);
						} finally {
							DicrDataModulePanel.this.log.addMessage("Завершено.");
						}
					}
				} else if (src == DicrDataModulePanel.this.tabCreateButton) {
					List<Throwable> errors = null;
					try {
						DicrDataModulePanel.this.log.addMessage("\nСоздание таблиц...");
						errors = DicrDataModulePanel.this.datamodule.createAllTables();
					} catch (Exception ex) {
						DicrDataModulePanel.this.log.addErrorMessage(ex);
					} finally {
						if (errors != null) DicrDataModulePanel.this.log.addErrorMessages(errors);
						DicrDataModulePanel.this.log.addMessage("Завершено.");
					}
				} else if (src == DicrDataModulePanel.this.tabOpenButton) {
					List<Throwable> errors = null;
					try {
						DicrDataModulePanel.this.log.addMessage("\nОткрытие таблиц...");
						errors = DicrDataModulePanel.this.datamodule.setDataSetsOpened(true);
					} catch (Exception ex) {
						DicrDataModulePanel.this.log.addErrorMessage(ex);
					} finally {
						if (errors != null) DicrDataModulePanel.this.log.addErrorMessages(errors);
						DicrDataModulePanel.this.log.addMessage("Завершено.");
					}
				} else if (src == DicrDataModulePanel.this.tabCloseButton) {
					List<Throwable> errors = null;
					try {
						DicrDataModulePanel.this.log.addMessage("\nЗакрытие таблиц...");
						errors = DicrDataModulePanel.this.datamodule.setDataSetsOpened(false);
					} catch (Exception ex) {
						DicrDataModulePanel.this.log.addErrorMessage(ex);
					} finally {
						if (errors != null) DicrDataModulePanel.this.log.addErrorMessages(errors);
						DicrDataModulePanel.this.log.addMessage("Завершено.");
					}
				} else if (src == DicrDataModulePanel.this.tabSaveButton) {
					List<Throwable> errors = null;
					try {
						DicrDataModulePanel.this.log.addMessage("\nПередача изменений в базу данных...");
						errors = DicrDataModulePanel.this.datamodule.saveAllDataSets();
					} catch (Exception ex) {
						DicrDataModulePanel.this.log.addErrorMessage(ex);
					} finally {
						if (errors != null) DicrDataModulePanel.this.log.addErrorMessages(errors);
						DicrDataModulePanel.this.log.addMessage("Завершено.");
					}
				} else if (src == DicrDataModulePanel.this.tabRefreshButton) {
					List<Throwable> errors = null;
					try {
						DicrDataModulePanel.this.log.addMessage("\nОбновление изменений...");
						errors = DicrDataModulePanel.this.datamodule.refreshAllDataSets();
					} catch (Exception ex) {
						DicrDataModulePanel.this.log.addErrorMessage(ex);
					} finally {
						if (errors != null) DicrDataModulePanel.this.log.addErrorMessages(errors);
						DicrDataModulePanel.this.log.addMessage("Завершено.");
					}
				} else if (src == DicrDataModulePanel.this.cachEnableButton
				        || src == DicrDataModulePanel.this.cachDisableButton) {
					try {
						boolean enable = (src == DicrDataModulePanel.this.cachEnableButton);
						DicrDataModulePanel.this.log.addMessage("\n" + (enable ? "Включение" : "Отключение")
						        + " локального кэша: " + DicrDataModulePanel.this.cachNameText.getText());
						if (enable) DicrDataModulePanel.this.datamodule.setCachFileName(DicrDataModulePanel.this.cachNameText.getText());
						DicrDataModulePanel.this.datamodule.setCachEnabled(enable);
					} catch (Exception ex) {
						DicrDataModulePanel.this.log.addErrorMessage(ex);
					} finally {
						DicrDataModulePanel.this.log.addMessage("Завершено.");
					}
				} else if (src == DicrDataModulePanel.this.cachChangeButton) {
					CustomFileFilter filter = new CustomFileFilter();
					filter.addExt("jds");
					filter.setDescription(".jds - файлы базы данных DataStore");
					JFileChooser chooser = new JFileChooser();
					chooser.setAcceptAllFileFilterUsed(true);
					chooser.setFileFilter(filter);
					String name = DicrDataModulePanel.this.datamodule.getCachFileName();
					if (name != null) {
						chooser.setSelectedFile(new File(name));
					}
					chooser.setDialogTitle("Выберите существующий или новый файл.");
					chooser.setDialogType(JFileChooser.SAVE_DIALOG);
					chooser.setApproveButtonText("Принять");
					if (chooser.showSaveDialog(DicrDataModulePanel.this) == JFileChooser.APPROVE_OPTION) {
						File file = chooser.getSelectedFile();
						if (file != null) {
							try {
								DicrDataModulePanel.this.log.addMessage("\nСмена имени файла кэша...");
								DicrDataModulePanel.this.datamodule.setCachFileName(file.getPath());
							} catch (Exception ex) {
								DicrDataModulePanel.this.log.addErrorMessage(ex);
							} finally {
								DicrDataModulePanel.this.log.addMessage("Завершено.");
							}
						}
					}
				} else if (src == DicrDataModulePanel.this.connSelectButton) {
					ConnectionElement elem = new ConnectionsFactoryPanel().showConnectionSelectDialog(
					        DicrDataModulePanel.this, null, DicrDataModulePanel.this.datamodule.getConnectionElement());
					if (elem != null) {
						try {
							DicrDataModulePanel.this.log.addMessage("\nУстановка параметров соединения: "
							        + elem.getName());
							DicrDataModulePanel.this.datamodule.setConnectionElement(elem);
						} catch (Exception ex) {
							DicrDataModulePanel.this.log.addErrorMessage(ex);
						} finally {
							DicrDataModulePanel.this.log.addMessage("Завершено.");
						}
					}
				} else if (src == DicrDataModulePanel.this.connConnectButton
				        || src == DicrDataModulePanel.this.connDisconnectButton) {
					try {
						boolean conn = (src == DicrDataModulePanel.this.connConnectButton);
						String op = conn ? "Подключение" : "Отключение";
						DicrDataModulePanel.this.log.addMessage("\n" + op + " соединения: "
						        + DicrDataModulePanel.this.datamodule.getConnectionElement().getName());
						DicrDataModulePanel.this.datamodule.setConnected(conn, false);
					} catch (Exception ex) {
						DicrDataModulePanel.this.log.addErrorMessage(ex);
					} finally {
						DicrDataModulePanel.this.log.addMessage("Завершено.");
					}
				}
			} catch (Exception ex) {
				DicrExceptionHandler.handleException(ex, this, true);
			} finally {
				setCursor(GuiMisc.CURSOR_DEFAULT);
			}
		}
	}

	/*******************************************************************************************************************
	 * ######################################################################### Слушатель модуля данных
	 * #########################################################################
	 ******************************************************************************************************************/
	class DataModuleListener implements ProgramListener {
		/**
		 * @see org.dicr.event.ProgramListener#eventPerformed(org.dicr.event.ProgramEvent)
		 */
		@Override
        public void eventPerformed(ProgramEvent e) {
			updateControls();
		}
	}

	/*******************************************************************************************************************
	 * Создает диалоговое окно
	 * 
	 * @param parent parent dialog
	 * @param theDatamodule data module
	 * @return created dialog
	 ******************************************************************************************************************/
	public DicrDialog createDialog(Component parent, DicrDataModule theDatamodule) {
		if (theDatamodule != null) this.setDataModule(theDatamodule);
		return new DicrDialog(parent, "Администратор модуля данных", DicrDialog.DIALOG_SIMPLE, this);
	}

	/*******************************************************************************************************************
	 * Выводит диалоговое окно
	 * 
	 * @param parent parent dialog
	 * @param theDatamodule data module
	 ******************************************************************************************************************/
	public void showDialog(Component parent, DicrDataModule theDatamodule) {
		DicrDialog dialog = createDialog(parent, theDatamodule);
		dialog.setVisible(true);
	}

	/*******************************************************************************************************************
	 * Показывает диалоговое окно
	 * 
	 * @param parent parent dialog
	 ******************************************************************************************************************/
	public void showDialog(Component parent) {
		showDialog(parent, null);
	}

	/**
	 * @see org.dicr.gui.DicrDialogContent#addContentListener(org.dicr.event.ProgramListener)
	 */
	@Override
    public void addContentListener(ProgramListener l) {
	//
	}

	/**
	 * @see org.dicr.gui.DicrDialogContent#removeContentListener(org.dicr.event.ProgramListener)
	 */
	@Override
    public void removeContentListener(ProgramListener l) {
	//
	}

	/*******************************************************************************************************************
	 * Освобождает ресурсы при закрытии диалога
	 ******************************************************************************************************************/
	@Override
    public void shutdown() {
		this.setDataModule(null);
	}

}
